%{ 
 
  /*******************************
   ** Copyleft:			**
   ** Daskalakis Dimitris	**
   ** Parasxos   Alexandros	**
   ** Pierris	 Georgios	**
   ** Version 	 0.1		**
   *******************************/

 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <math.h>
 
 
 #define IMEM_SIZE 1024
 #define LABEL_SIZE 50 
 
 extern int numline;
 
 int InstrNum = 0;
 
 typedef struct {
 	unsigned int aInstr;
 	char aLabel[LABEL_SIZE];
 } InstrType;
 
 InstrType Instr[IMEM_SIZE];
 
 int LabelNum = 0;
 
 typedef struct {
 	char Name[LABEL_SIZE];
 	int InstrLine;
 } LabelType;
 
 
 LabelType Label[300];
 
 typedef struct {
 	unsigned int add;
 	unsigned int sub;
 	unsigned int or;
 	unsigned int and;
 	unsigned int not;
 	unsigned int slt;
 	unsigned int jr;
 	unsigned int jalr;
 } FuncCodeType;
 
 FuncCodeType FuncCode;
 
 typedef struct {
 	unsigned int rType;
 	unsigned int addi;
 	unsigned int subi;
 	unsigned int andi;
 	unsigned int ori;
 	unsigned int slti;
 	unsigned int lui;
 	unsigned int beqz;
 	unsigned int bneq;
 	unsigned int lw;
 	unsigned int sw;
 	unsigned int j;
 	unsigned int jal;
 } OpCodeType;
 
 OpCodeType OpCode;

%}

%union {
	int val;
	char* str;
}


%token TK_add
%token TK_sub
%token TK_and
%token TK_not
%token TK_or
%token TK_slt
%token TK_jr
%token TK_jalr
%token TK_jal
%token TK_j
%token TK_addi
%token TK_subi
%token TK_andi
%token TK_ori
%token TK_slti
%token TK_lui
%token TK_beqz
%token TK_bneq
%token TK_lw
%token TK_sw

%token TK_nop
%token TK_mov

%token<val> TK_immed_disp
%token<val> TK_reg

%token<str> TK_new_label
%token<str> TK_label


%start instructions

%%



instructions:									
		|	rType 						{ InstrNum++; } 
			instructions
		|	iType 						{ InstrNum++; }
			instructions
		|	jType 						{ InstrNum++; }
			instructions
		|	TK_new_label 		 			{ 
									  strncpy(Label[LabelNum].Name,$1,strlen($1)-1);
									  Label[LabelNum].InstrLine = InstrNum;
									  LabelNum++;
									}
			instructions
		|	TK_nop						{ InstrNum++; }
			instructions
;

rType3reg:		TK_add						{ Instr[InstrNum].aInstr += FuncCode.add; } 
		| 	TK_sub						{ Instr[InstrNum].aInstr += FuncCode.sub; } 
		| 	TK_and 						{ Instr[InstrNum].aInstr += FuncCode.and; }
		| 	TK_or 						{ Instr[InstrNum].aInstr += FuncCode.or;  }
		| 	TK_slt						{ Instr[InstrNum].aInstr += FuncCode.slt; } 
;

rType2reg:		TK_jalr 					{ Instr[InstrNum].aInstr += FuncCode.jalr; }
		| 	TK_not 						{ Instr[InstrNum].aInstr += FuncCode.not;  }
		|	TK_mov						{ Instr[InstrNum].aInstr += FuncCode.add;  }
;

rType1reg:		TK_jr						{ Instr[InstrNum].aInstr += FuncCode.jr; }
;


rType:			rType3reg TK_reg TK_reg TK_reg 			{ Instr[InstrNum].aInstr += ($4<<21) + ($3<<16) + ($2<<11); }
		|	rType2reg TK_reg TK_reg				{ Instr[InstrNum].aInstr += ($3<<21) + ($2<<11); }
		|	rType1reg TK_reg				{ Instr[InstrNum].aInstr += ($2<<21); }
;


iType2reg:		TK_addi						{ Instr[InstrNum].aInstr += (OpCode.addi<<26); }
		|	TK_subi						{ Instr[InstrNum].aInstr += (OpCode.subi<<26); }
		|	TK_andi						{ Instr[InstrNum].aInstr += (OpCode.andi<<26); }
		|	TK_ori						{ Instr[InstrNum].aInstr += (OpCode.ori<<26);  }
		|	TK_slti						{ Instr[InstrNum].aInstr += (OpCode.slti<<26); }
		|	TK_lw						{ Instr[InstrNum].aInstr += (OpCode.lw<<26);   }
		|	TK_sw						{ Instr[InstrNum].aInstr += (OpCode.sw<<26);   }
;

iType1reg:		TK_lui						{ Instr[InstrNum].aInstr += (OpCode.lui<<26); }
;

iTypeBranch:		TK_beqz 					{ Instr[InstrNum].aInstr += (OpCode.beqz<<26); }
		|	TK_bneq						{ Instr[InstrNum].aInstr += (OpCode.bneq<<26); }
;

iType:			iType2reg TK_reg TK_reg TK_immed_disp		{ Instr[InstrNum].aInstr += ($3<<21) + ($2<<16) + ($4 & 0xFFFF); }
		|	iType1reg TK_reg TK_immed_disp			{ Instr[InstrNum].aInstr += ($3<<21) + ($2 & 0xFFFF); }
		|	iTypeBranch TK_reg TK_label			{  
									  Instr[InstrNum].aInstr += ($2<<21);
									  strcpy(Instr[InstrNum].aLabel,$3);
									}
;

jType:			TK_jal	TK_label 				{ Instr[InstrNum].aInstr += (OpCode.jal<<26);
									  strcpy(Instr[InstrNum].aLabel,$2);
									}
		|	TK_j	TK_label				{ 
									  Instr[InstrNum].aInstr += (OpCode.j<<26); 
									  strcpy(Instr[InstrNum].aLabel,$2);
									}
;

%%

void labels2addr();
void initFromFile (char* filename);
void writeToFile (char* filename);
char* int2bin (unsigned int a,char* res);
int bin2int (char* binary);
int searchLabel(char* labelName);

int main(int argc,char* argv[]){
	if (argc!=3 && argc!=5) {
		fprintf(stderr,"Usage %s -o <output_file> [-c <config_file>] '<' <input_file> \n",argv[0]);
		exit(-1);
	}
	if (argc==5)
		initFromFile(argv[4]);
	else
		initFromFile("config.txt");
	yyparse();
	labels2addr();
	writeToFile(argv[2]);
	return 0;
}

int yyerror(char* s) {
	fprintf(stderr,"Syntax error found near line: %d\n",numline);
}

int searchLabel(char* labelName) {
	int i=0;
	for (i=0; i<LabelNum; i++) 
		if ( ! strcmp(Label[i].Name,labelName) ) 
			return Label[i].InstrLine;
	fprintf(stderr,"Label \"%s\" not found\n",labelName);
	exit(-1);
	return -1;
}

void labels2addr() {
	int i;
	for (i=0; i<InstrNum; i++) {
		unsigned int tempOpCode = Instr[i].aInstr>>26;
		if (tempOpCode==OpCode.j || tempOpCode==OpCode.jal)
			Instr[i].aInstr += searchLabel(Instr[i].aLabel);
		else if (tempOpCode==OpCode.beqz || tempOpCode==OpCode.bneq)
			Instr[i].aInstr +=( (searchLabel(Instr[i].aLabel) - (i+1)) & 0xFFFF);
	}
}


void initFromFile (char* filename) {
	FILE * InputFile = fopen(filename, "r");
	char line[100];
	char * tempOp;
	char * tempOpFuncCode;
	if (InputFile==NULL) {
		fprintf(stderr,"Can't open \"%s\" configuration file \n",filename);
		exit(-1);
	}
	while(fgets(line,100,InputFile)!=NULL){
		if (line[0]=='/')
			continue;
		tempOp = strtok(line," ");
		tempOpFuncCode = strtok(NULL," ");
		if (strcmp(tempOp,"add")==0)
			FuncCode.add=bin2int(tempOpFuncCode);
		else if (strcmp(tempOp,"sub")==0)
			 FuncCode.sub=bin2int(tempOpFuncCode);
		else if (strcmp(tempOp,"or")==0)
			 FuncCode.or=bin2int(tempOpFuncCode);
		else if (strcmp(tempOp,"and")==0)
			 FuncCode.and=bin2int(tempOpFuncCode);
		else if (strcmp(tempOp,"not")==0)
			 FuncCode.not=bin2int(tempOpFuncCode);
		else if (strcmp(tempOp,"slt")==0)
			 FuncCode.slt=bin2int(tempOpFuncCode);
		else if (strcmp(tempOp,"jr")==0)
			 FuncCode.jr=bin2int(tempOpFuncCode);
		else if (strcmp(tempOp,"jalr")==0)
			 FuncCode.jalr=bin2int(tempOpFuncCode);
		else if (strcmp(tempOp,"jal")==0)
			 OpCode.jal=bin2int(tempOpFuncCode);
		else if (strcmp(tempOp,"j")==0)
			 OpCode.j=bin2int(tempOpFuncCode);
		else if (strcmp(tempOp,"addi")==0)
			 OpCode.addi=bin2int(tempOpFuncCode);
		else if (strcmp(tempOp,"subi")==0)
			 OpCode.subi=bin2int(tempOpFuncCode);
		else if (strcmp(tempOp,"andi")==0)
			 OpCode.andi=bin2int(tempOpFuncCode);
		else if (strcmp(tempOp,"ori")==0)
			 OpCode.ori=bin2int(tempOpFuncCode);
		else if (strcmp(tempOp,"slti")==0)
			 OpCode.slti=bin2int(tempOpFuncCode);
		else if (strcmp(tempOp,"lui")==0)
			 OpCode.lui=bin2int(tempOpFuncCode);
		else if (strcmp(tempOp,"beqz")==0)
			 OpCode.beqz=bin2int(tempOpFuncCode);
		else if (strcmp(tempOp,"bneq")==0)
			 OpCode.bneq=bin2int(tempOpFuncCode);
		else if (strcmp(tempOp,"lw")==0)
			 OpCode.lw=bin2int(tempOpFuncCode);
		else if (strcmp(tempOp,"sw")==0)
			 OpCode.sw=bin2int(tempOpFuncCode);
	}
}

void writeToFile (char * filename) {
	int i=0;
	char CurInstr[33];
	FILE * OutputFile = fopen(filename,"w");
	CurInstr[32]='\0';
	if (OutputFile==NULL) {
		fprintf (stderr,"Can't create output file\n");
		return;
	}
	fprintf(OutputFile,"MEMORY_INITIALIZATION_RADIX=2;\n");
	fprintf(OutputFile,"MEMORY_INITIALIZATION_VECTOR=\n");
	for (i=0;i<1023;i++) 
		fprintf (OutputFile,"%s,\n",int2bin(Instr[i].aInstr,CurInstr));
	fprintf (OutputFile,"%s;",int2bin(Instr[1023].aInstr,CurInstr));
	fclose(OutputFile);
}


char* int2bin (unsigned int a,char* res) {
	char * tmp = res + 31*sizeof(char);
	for (;tmp>=res;tmp--) {
		(*tmp) = '0'+a%2;
		a /= 2;
	}
	return res;
}

int bin2int (char* binary){
	int res=0;
	int i;
	for (i=0;i<6;i++)
		res+=(binary[5-i]-'0')*pow(2,i);
	return res;
}

